﻿using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RaytracerLib;
using RaytracerLib.MathLib;
using TomanuExtensions;

namespace RaytracerTest
{
    [TestClass]
    public class Vector3Test : TestBase
    {
        [TestMethod]
        public void Vector3_Test()
        {
            double[] ar = { 0, 1, -1, 2, -2, 1000000, -1000000 };

            var perm = from v1 in ar
                       from v2 in ar
                       from v3 in ar
                       select new { v1, v2, v3 };

            foreach (var p1 in perm)
            {
                var v1d = new Microsoft.DirectX.Vector3((float)p1.v1, (float)p1.v2, (float)p1.v3);
                var v1 = new Vector3(p1.v1, p1.v2, p1.v3);

                foreach (var p2 in perm)
                {
                    var v2d = new Microsoft.DirectX.Vector3((float)p2.v1, (float)p2.v2, (float)p2.v3);
                    var v2 = new Vector3(p2.v1, p2.v2, p2.v3);

                     // Add
                     {
                         var v3 = v1 + v2;
                         var v3d = v1d + v2d;
                         AreAlmostRelativeEquals(v3d, v3);
                     }

                     // Div
                     {
                         if (v2.X == 0)
                             continue;
                         if (v2.Y == 0)
                             continue;
                         if (v2.Z == 0)
                             continue;
                         var v3 = v1 / v2;
                         var v3d = new Microsoft.DirectX.Vector3(
                             (float)(v1.X / v2.X), 
                             (float)(v1.Y / v2.Y), 
                             (float)(v1.Z / v2.Z));
                         AreAlmostRelativeEquals(v3d, v3);
                     }
                     
                     // Sub
                     {
                         var v3 = v1 - v2;
                         var v3d = v1d - v2d;
                         AreAlmostRelativeEquals(v3d, v3);
                     }
                     
                     // Dot
                     {
                         var r = v1 * v2;
                         var rd = Microsoft.DirectX.Vector3.Dot(v1d, v2d);
                         AreAlmostRelativeEquals(rd, r);
                     }

                     // Minimize
                     {
                         var r = Vector3.Minimize(v1, v2);
                         var rd = Microsoft.DirectX.Vector3.Minimize(v1d, v2d);
                         AreAlmostRelativeEquals(rd, r);
                     }

                     // Maximize
                     {
                         var r = Vector3.Maximize(v1, v2);
                         var rd = Microsoft.DirectX.Vector3.Maximize(v1d, v2d);
                         AreAlmostRelativeEquals(rd, r);
                     }
                     
                     // Cross
                     {
                         var v3 = Vector3.CrossProduct(v1, v2);
                         var v3d = Microsoft.DirectX.Vector3.Cross(v1d, v2d);
                         AreAlmostRelativeEquals(v3d, v3);
                     }
                     
                     // Scale 1
                     {
                         var v3 = v1 * v2.X;
                         var v3d = v1d * v2d.X;
                         AreAlmostRelativeEquals(v3d, v3);
                     }

                     // Scale 2
                     {
                         var v3 = v2.X * v1;
                         var v3d = v2d.X * v1d;
                         AreAlmostRelativeEquals(v3d, v3);
                     }

                     // Div const
                     {
                         var v3 = v1 / v2.X;
                         var v3d = v1d *  (1 / v2d.X);
                         AreAlmostRelativeEquals(v3d, v3);
                     }
                     
                     // Neg
                     {
                         var v3 = -v1;
                         var v3d = -v1d;
                         AreAlmostRelativeEquals(v3d, v3);
                     }
                     
                     // Normalize
                     {
                         var v3 = v1.Normalized;
                         var v3d = Microsoft.DirectX.Vector3.Normalize(v1d);
                         AreAlmostRelativeEquals(v3d, v3);
                     
                         if (v3.SqrLen != 0)
                             Assert.IsTrue(v3.IsNormalized);
                     }
                     
                    // Length
                    {
                        var r = v1.Length;
                        var rd = v1d.Length();
                        Assert.IsTrue(rd.IsAlmostRelativeEquals((float)r));
                    
                        r = v1.SqrLen;
                        rd = v1d.LengthSq();
                        Assert.IsTrue(rd.IsAlmostRelativeEquals((float)r));
                    }

                    // Less.
                    {
                        var v3 = v1 < v2;
                        var v3d = (v1d.X < v2d.X) && (v1d.Y < v2d.Y) && (v1d.Z < v2d.Z);
                        Assert.AreEqual(v3d, v3);
                    }

                    // Less or eq.
                    {
                        var v3 = v1 <= v2;
                        var v3d = (v1d.X <= v2d.X) && (v1d.Y <= v2d.Y) && (v1d.Z <= v2d.Z);
                        Assert.AreEqual(v3d, v3);
                    }

                    // Greater.
                    {
                        var v3 = v1 > v2;
                        var v3d = (v1d.X > v2d.X) && (v1d.Y > v2d.Y) && (v1d.Z > v2d.Z);
                        Assert.AreEqual(v3d, v3);
                    }

                    // Greater or eq.
                    {
                        var v3 = v1 >= v2;
                        var v3d = (v1d.X >= v2d.X) && (v1d.Y >= v2d.Y) && (v1d.Z >= v2d.Z);
                        Assert.AreEqual(v3d, v3);
                    }
                    
                    // Equality.
                    {
                        bool b1 = v1d.Equals(v2d);
                        bool b2 = v1d == v2d;
                        bool b3 = v1.Equals(v2);
                        bool b4 = v1 == v2;
                    
                        Assert.IsTrue(b1 == b2);
                        Assert.IsTrue(b2 == b3);
                        Assert.IsTrue(b3 == b4);
                    }
                }
            }

            // Equal.
            {
                var m1 = new Matrix3(0, 0, 0, 0, 0, 0, 0, 0, 0);
                var m2 = new Matrix3(0, 0, 0, 0, 0, 0, 0, 0, 0);
                var ar1 = m1.ToArray();

                Assert.IsTrue(m1.Equals(m1));

                for (int x = 0; x < 3; x++)
                {
                    for (int y = 0; y < 3; y++)
                    {
                        ar1[x + y * 3] = 1;
                        m1 = new Matrix3(ar1);

                        Assert.IsFalse(m1 == m2);
                        Assert.IsTrue(m1 != m2);
                        Assert.IsFalse(m1.Equals(m2));

                        m2 = new Matrix3(ar1);

                        Assert.IsTrue(m1 == m2);
                        Assert.IsFalse(m1 != m2);
                        Assert.IsTrue(m1.Equals(m2));

                        ar1[x + y * 3] = 0;
                        m1 = new Matrix3(ar1);
                        m2 = new Matrix3(ar1);
                    }
                }
            }
        }
    }
}